<?php

namespace app\common\model;

use think\Model;
use think\Hook;
use think\Loader;
use Carbon\Carbon;
use think\Lang;
use think\Request;
use think\Exception;
use app\common\traits\model\TraitModel;

class Calendar extends Model
{
    protected $table              = 'user_caravan_calendar';
    protected $autoWriteTimestamp = 'timestamp';
    protected $createTime         = 'created_at';
    protected $updateTime         = 'updated_at';
    protected $resultSetType      = 'collection';
    const DAYS_AHEAD              = 1;//提前几天设置
    const MINIMUM_PEAK_DATE       = 15;//最少强制分享天数

    use TraitModel;

    public function caravans()
    {
        return $this->belongsTo('Caravans', 'caravan_id');
    }

    public function user()
    {
        return $this->belongsTo('Users', 'user_id');
    }

    /**
     * 初始化房车日历
     * @param  [type]  $caravan_info [description]
     * @param  [type]  $init_dates   [description]
     * @param  integer $years        [description]
     * @return [type]                [description]
     */
    public function initCaravanCarlendar($init_dates, $years = 1)
    {
        $diffday = $this->diffDays($this->where(['caravan_id' => Request::instance()->caravan->id])->order('datetimes', 'desc')->value('datetimes') ? : Carbon::now(), Carbon::now()->addYear($years), false);

        if ($diffday <= 0) {
            return true;
        }

        $Holidays = self::getCoefficient(self::createDateRange(Carbon::now()->addDay(self::DAYS_AHEAD), Carbon::now()->addDay(self::DAYS_AHEAD)->addYear($years)), false);

        for ($i = 0; $i < $diffday; $i++) {
            $datetimes           = Carbon::now()->addDay(self::DAYS_AHEAD + $i)->toDateString();
            $is_holiday          = isset($Holidays[$datetimes]) ? $Holidays[$datetimes] : 0;
            $holiday_coefficient = \app\common\model\Holidays::$is_holiday[$is_holiday];

            $datas[] = [
                'caravan_id'       => Request::instance()->caravan->id, 
                'user_id'          => Request::instance()->user->id, 
                'camps_id'         => Request::instance()->caravan->camp->id, 
                'single_day_price' => Request::instance()->caravan->camp->rent_money_base * $holiday_coefficient,
                'single_day_coin'  => Request::instance()->caravan->camp->travel_money_base * $holiday_coefficient,
                'is_holiday'       => $is_holiday, 
                'datetimes'        => $datetimes
            ];
        }

        $this->startTrans();
        try {
            $this->createCaravanCarlendars($datas);
            $this->shareCaravanBusySeason($init_dates['start_date'], $init_dates['end_date']); 
            $this->commit();
            return true;
        } catch (\Exception $e) {
            $this->rollback();
            throw $e;
        }    
        return false;    
    }

    public function createCaravanCarlendars($datas)
    {
        if (key($datas) === 0) {
            $infos = $datas;
        }else{
            $infos[] = $datas;
        }
        return $this->allowField(true)->saveAll($datas);
    }

    public static function setWhere($query = [])
    {
        $start_date = $query['start_date'];
        $end_date = isset($query['end_date']) ? $query['end_date'] : '';
        

        if (!is_array($start_date)) {
            if (strpos($start_date, ',')) {
                $start_date = explode(',', $start_date);
            }else{
                $end_date = $end_date ? : $start_date;  
            }
        }
        unset($query['start_date'], $query['end_date']);

        if (is_array($start_date)) {
            $query['datetimes'] = ['in', $start_date];
        }else{
            $query['datetimes'] = ['between', [$start_date, $end_date]];
        }

        return $query;
    }

    /**
     * 分享房车旺季
     * @param  [type] $start_date [description]
     * @param  [type] $end_date   [description]
     * @return [type]             [description]
     */
    private function shareCaravanBusySeason($start_date, $end_date = null)
    {
        if (!$start_date) {
            throw new Exception(Lang::get("Start date cannot be empty"), 50008);
        }

        $end_date = $end_date ? : $start_date;
        if (!$this->checkPeakDate($start_date, $end_date)) {
            throw new Exception(Lang::get("Please share peak time"), 50001); 
        }

        $this->startTrans();
        try {
            $this->setCaravanStatus(Request::instance()->caravan->id, ['shares' => 1, 'trusts' => 0, 'locks' => 1], $start_date, $end_date);
            $this->commit();
            
        } catch (\Exception $e) {
            $this->rollback();
            throw $e;            
        }
        return true;
    }

    /**
     * 设置房车日历状态为分享
     * @param [type] $start_date [description]
     * @param [type] $end_date   [description]
     */
    public function setCaravanShare($start_date, $end_date = null)
    {
        if (!$start_date) {
            throw new Exception(Lang::get("Start date cannot be empty"), 50008);
        }

        $caravan_id = Request::instance()->caravan->id;
        $end_date   = $end_date ? : $start_date;

        if ($this->checkCalendarLock($caravan_id, $start_date, $end_date)) {
            throw new Exception(Lang::get("Lock date exists"), 50002);                
        }

        $this->startTrans();
        try {
            $this->setCaravanStatus($caravan_id, ['shares' => 1, 'trusts' => 0], $start_date, $end_date);
            $this->commit();
            
        } catch (\Exception $e) {
            $this->rollback();
            throw $e;            
        }
        return true;
    }

    public function setCaravanOrder($start_date, $end_date = null, $orders = true)
    {
        if (!$start_date) {
            throw new Exception(Lang::get("Start date cannot be empty"), 50008);
        }

        $caravan_id = Request::instance()->caravan->id;
        $end_date   = $end_date ? : $start_date;

        $this->startTrans();
        try {
            $querys = [
                'start_date' => $start_date, 
                'end_date'   => $end_date, 
                'caravan_id' => $caravan_id,
                'orders'     => $orders ? 0 : 1
            ];

            $update_info = [
                'orders' => $orders ? 1 : 0
            ];

            self::where(self::setWhere($querys))->update($update_info);
            $this->refreshCaravanStatuDateLine($caravan_id);
            $this->commit();
            
        } catch (\Exception $e) {
            $this->rollback();
            throw $e;            
        }
        return true;
    }

    /**
     * 设置房车日历状态为自用状态
     * @param [type] $start_date [description]
     * @param [type] $end_date   [description]
     */
    public function setCaravanHome($start_date, $end_date = null)
    {
        if (!$start_date) {
            throw new Exception(Lang::get("Start date cannot be empty"), 50008);
        }

        $caravan_id = Request::instance()->caravan->id;
        $end_date   = $end_date ? : $start_date;

        if ($this->checkCalendarLock($caravan_id, $start_date, $end_date)) {
            throw new Exception(Lang::get("Lock date exists"), 50002);                
        }

        $this->startTrans();
        try {
            $this->setCaravanStatus($caravan_id, ['shares' => 0, 'trusts' => 0], $start_date, $end_date);
            $this->commit();
            
        } catch (\Exception $e) {
            $this->rollback();
            throw $e;            
        }
        return true;
    }

    /**
     * 设置房车日历状态为托管状态
     * @param [type]  $start_date [description]
     * @param [type]  $end_date   [description]
     * @param boolean $shares     [混合]
     */
    public function setCaravanTrusteeship($start_date, $end_date = null, $shares = true)
    {
        if (!$start_date) {
            throw new Exception(Lang::get("Start date cannot be empty"), 50008);
        }
        
        $caravan_id = Request::instance()->caravan->id;
        $end_date   = $end_date ? : $start_date;

        $this->startTrans();
        try {
            $this->setCaravanStatus($caravan_id, ['shares' => $shares ? 1 : 0, 'trusts' => 1, 'locks' => 1], $start_date, $end_date);
            $this->commit();
            
        } catch (\Exception $e) {
            $this->rollback();
            throw $e;            
        }
        return true;
    }

    private function setCaravanStatus($caravan_id, $update_info, $start_date, $end_date)
    {
        if (self::DAYS_AHEAD > self::diffDays(Carbon::now(), $start_date, false)) {
            throw new Exception(Lang::get("Please set in advance", [self::DAYS_AHEAD]), 50004);            
        }

        $end_date = $end_date ? : $start_date;  
        if (self::diffDays($start_date, $end_date, false) < 0) {
            throw new Exception(Lang::get("End time must be greater than or equal to the start time"), 50005);      
        }

        $querys = [
            'start_date' => $start_date, 
            'end_date'   => $end_date, 
            'caravan_id' => $caravan_id,
            'orders'     => 0,
        ];

        $_update_info = [
            'shares' => 0,
            'trusts' => 0,
            'locks'  => 0
        ];

        if(self::where(self::setWhere($querys))->update(array_merge($_update_info, $update_info))){
            //$this->refreshCaravanStatuDateLine($caravan_id);
        }
        $this->refreshCaravanStatuDateLine($caravan_id);
    }

    /**
     * 刷新房车状态时间段
     * @param  [type] $caravan_info [description]
     * @return [type]               [description]
     */
    protected function refreshCaravanStatuDateLine($caravan_id = null)
    {
        $caravan_info = $caravan_id ? Loader::model('Caravans')->get($caravan_id) : Request::instance()->caravan;
        $caravan_id   = $caravan_info->id;

        $datetimes          = $this->getCaravanStatus('shares');
        $start_date         = Carbon::now()->addDay(1)->toDateString();        
        $car_calendar_model = Loader::model('CarCalendar');
        $car_calendar_model->where(['s' => ['LT', $start_date]])->delete();
        //$sql                = "`caravan_id` = " . $caravan_id . " AND (";
        $s = [];
        foreach ($datetimes as $key => $datetime) {
            $dates = self::combination($datetime);
            if (is_array($dates)) {
                foreach ($dates as $date) {
                    $new_datas[] = [
                        'sign'       => $key,
                        'caravan_id' => $caravan_id, 
                        's'          => $date['start'], 
                        'e'          => $date['end']
                    ];

                    $s[] = ['sign' => $key, 's' => $date['start'], 'e' => $date['end']];

                    //$sql .= "(`sign` = '" . $key . "' AND `s` = '" . $date['start'] . "' AND `e` = '" . $date['end'] . "') OR ";
                }
            }                        
        }
        $old_datas = $car_calendar_model->where(['caravan_id' => $caravan_id])
            ->where(
                function($q) use ($s) {
                    foreach ($s as $a) {
                        $q->whereOr(
                            function($q) use ($a) {
                                $q->where($a);
                            }
                        );
                    }
                }
            )
            ->field('sign,s,e,caravan_id,id')
            ->select();
        //$old_datas = $car_calendar_model->where(rtrim(trim($sql), 'OR') . ')')->field('sign,s,e,caravan_id,id')->select();

        foreach ($old_datas as $old_data) {
            $o_ids[] = $old_data->id;
            unset($old_data->id);
        }

        foreach ($new_datas as $key => &$new_data) {
            if (in_array($new_data, $old_datas->toArray())) {
                unset($new_datas[$key]);
                continue;
            }

            $new_data['user_id']   = $caravan_info->user_id;
            $new_data['camp_id']   = $caravan_info->camp->id;
            $new_data['branch_id'] = $caravan_info->branch_id;
            $new_data['car_area']  = $caravan_info->area;
        }

        isset($o_ids) ? Loader::model('CarCalendar')->where(['caravan_id' => $caravan_id, 'id' =>['not in', $o_ids]])->delete() : '';
        Loader::model('CarCalendar')->allowField(true)->saveAll($new_datas);
    }

    protected function checkCalendarLock($caravan_id, $start_date, $end_date)
    {
        $querys = [
            'start_date' => $start_date, 
            'end_date'   => $end_date, 
            'caravan_id' => $caravan_id,
            'locks'      => 1,
        ];

        return $this->where(self::setWhere($querys))->count() ? true : false;
    }

    /**
     * 校验所选时间是否分享状态
     * @param  [type] $start_date [description]
     * @param  [type] $end_date   [description]
     * @return [type]             [description]
     */
    public function checkCalendarShared($start_date, $end_date)
    {
        $caravan_id = Request::instance()->caravan->id;
        $end_date   = $end_date ? : $start_date;

        $querys = [
            'start_date' => $start_date, 
            'end_date'   => $end_date, 
            'caravan_id' => $caravan_id
        ];

        $sign     = 1;
        $where_or = [
            'shares' => ['NEQ', $sign], 
            'orders' => $sign
        ];

        return $this->where(self::setWhere($querys))->where(function($query) use ($where_or){
                    $query->whereOr($where_or);
                })->count() ? false : true;
    }

    /**
     * 校验强制分享时间段是否合法
     * @param  [type] $start_date [description]
     * @param  [type] $end_date   [description]
     * @return [type]             [description]
     */
    public function checkPeakDate($start_date, $end_date)
    {
        $peak_date = Request::instance()->caravan->camp->peak_date;

        if (!$peak_date) {
            throw new Exception(Lang::get("Camp is not set the peak date"), 50006);
        }

        $end_date = $end_date ? : $start_date;

        if (key($peak_date) === 0) {
            $peak_dates = $peak_date;
        }else{
            $peak_dates[] = $peak_date;
        }

        foreach ($peak_dates as $date) {
            $date_one  = $this->comparisonDate($start_date, $date['start_date']) == 'gt' ? $start_date : $date['start_date'];
            $date_two  = $this->comparisonDate($end_date, $date['end_date']) == 'gt' ? $date['end_date'] : $end_date;
            if (self::diffDays($date_one, $date_two, false) >= self::MINIMUM_PEAK_DATE) {
                return true;
            }
        }

        return false;
    }

    /**
     * 校验房车资源是否可以订单支付
     * @param  string $start_date [description]
     * @param  string $end_date   [description]
     * @return [type]             [description]
     */
    public function checkCanPay($caravan_id, $start_date, $end_date = null)
    {
        if (!$caravan_id) {
            throw new Exception(Lang::get("RV ID cannot be empty"), 50007);
        }

        if (!$start_date) {
            throw new Exception(Lang::get("Start date cannot be empty"), 50008);
        }

        $end_date = $end_date ? : $start_date;
        return $this->where(['caravan_id' => $caravan_id, 'datetimes' => ['BETWEEN', [$start_date, $end_date]]])
                    ->where(
                        function($query){
                            $query->where(['orders' => 1])
                                  ->whereOr(
                                        function($query){
                                            $query->where(['trusts' => 1, 'shares' => 0]);
                                        }
                                    );
                        }
                    )
                    ->lock(true)
                    ->count() ? false : true;
    }

    /**
     * 获取房车状态数组
     * @param  array/string $sign [description]
     * @return array              [description]
     * @throws think\Exception    
     */
    public function getCaravanStatus($sign = null)
    {
        $caravan_id = Request::instance()->caravan->id;

        $res   = [
            //'homes'    => [], 
            'shares'   => [], 
            'trusts'   => [], 
            'disables' => [
                'shares'        => [],
                'trusts'        => [],
                'trustsnoorder' => []

            ]
        ];
        $start_date = Carbon::now()->addDay(1)->toDateString();
        $where      = [
            'caravan_id' => $caravan_id,
            //'orders'     => 0,  
            'datetimes'  => ['EGT', $start_date]
        ];

        if ($sign) {
            if (is_string($sign)) {
                $sign = explode(',', $sign);
            }
            $sign = array_intersect_key($res, array_flip($sign));
        }else{
            $sign = $res;
        }

        $sign = array_keys($sign);
        $caravanCalendars = $this->where($where)->select();

        if (!$caravanCalendars->isEmpty()) {
            foreach ($caravanCalendars as $calendar) {
                if ($calendar->shares == 0 && $calendar->trusts == 0) {
                    //$res['homes'][] = $calendar->datetimes;
                }
                if (in_array('shares', $sign) && $calendar->shares == 1 && $calendar->orders == 0) {
                    $res['shares'][] = $calendar->datetimes;
                }

                if (in_array('trusts', $sign) && $calendar->shares == 0 && $calendar->trusts == 1 && $calendar->orders == 0) {
                    $res['trusts'][] = $calendar->datetimes;
                }

                if (in_array('disables', $sign) && ($calendar->orders == 1 || $calendar->trusts == 1)) {
                    if ($calendar->orders) {
                        if ($calendar->shares) {
                            $res['disables']['shares'][] = $calendar->datetimes;
                        }
                        if ($calendar->trusts) {
                            $res['disables']['trusts'][] = $calendar->datetimes;
                        }
                    }elseif ($calendar->trusts) {
                        $res['disables']['trustsnoorder'][] = $calendar->datetimes;
                    }
                    
                    //$res['disables'][] = $calendar->datetimes;
                }  
            }

            return array_intersect_key($res, array_flip($sign));
        }
        throw new Exception(Lang::get("CaravanCalendars info empty"), 50010);
    }
}
